﻿/// <reference path="../../js/Data Access/FolioFixer_DataSources.js" />
/// <reference path="../../Highcharts/js/highcharts.src.js" />
/// <reference path="../../js/Data Access/FolioFixer_Calculators.js" />
/// <reference path="../../js/Data Access/ResourceManager.js" />
// For an introduction to the Page Control template, see the following documentation:
// http://go.microsoft.com/fwlink/?LinkId=232511
(function () {
    "use strict";

    var dataSource = null;
    var weights = {};
    var portfolioCalc;

    var publicMembers = { DataSource: dataSource, Weights: weights };
    WinJS.Namespace.define("FolioFixer.PortfolioPage", publicMembers);

    WinJS.UI.Pages.define("/pages/portfoliospecs/portfoliospecs.html", {
        // This function is called whenever a user navigates to this page. It
        // populates the page elements with the app's data.
        ready: function (element, options) {
            var currentState = WinJS.Navigation.state;
            
            if (currentState) {
                $(".pagetitle").text(currentState.portfolio.name + " Specifics");
            }

            btnCAPM.addEventListener("click", calculateCAPM);
            btnHistorical.addEventListener("click", calculateHistorical);
            btnOptimal.addEventListener("click", calculateOptimal);

            portfolioCalc = new FolioFixer.Calculators.PortfolioCalculator(currentState.portfolio, false);
            
            simpleToggle.winControl.checked = false;
            simpleToggle.winControl.addEventListener("change",
                function () {
                    loadingFlyout.winControl.show(loadingAnchor);
                    portfolioCalc.setSimple(simpleToggle.winControl.checked);
                    
                    loadingFlyout.winControl.hide();

                    refresh();
                }
            );

            startdatepicker.winControl.addEventListener("change",
                function () {
                    var date = startdatepicker.winControl.current;
                    date = new FolioFixer.DataTypes.Date(date.getFullYear(), date.getMonth(), date.getDate());
                    publicMembers.DataSource.portfolioCalculator.getMinDate().done(
                        function (minDate) {
                            if (date.getValue() != minDate.getValue()) {
                                publicMembers.DataSource.setDate(date);
                                publicMembers.DataSource.portfolioCalculator.getMinDate().done(
                                    function (minDate2) {
                                        setDate(minDate2);
                                    }
                                );
                            }
                        }
                    );

                    refresh();
                }
            );

            loadingFlyout.winControl.show(loadingAnchor);
            portfolioCalc.getTotalAnnualizedStandardDeviationWeight().done(function () { loadingFlyout.winControl.hide(); });

            var assetDataSource = new FolioFixer.DataSources.AssetDataSource(portfolioCalc, 1);

            var list = document.querySelector("#assetList");
            publicMembers.DataSource = assetDataSource;
            list.winControl.itemDataSource = publicMembers.DataSource;
            list.addEventListener("iteminvoked", navFromList);

            
            document.getElementById("addButton").addEventListener("click", doClickAdd, false);
            document.getElementById("confirmSymbol").addEventListener("click", addSymbol);

            updateUI();

            
        },

        unload: function () {
            // TODO: Respond to navigations away from this page.
        },

        updateLayout: function (element, viewState, lastViewState) {
            /// <param name="element" domElement="true" />

            // TODO: Respond to changes in viewState.
        }
    });

    function refresh() {
        publicMembers.DataSource.refresh();

        updateUI();
    }

    function updateUI() {
        portfolioCalc.getMinDate().done(function (date) {
            setDate(date);
        });

        generateFrontierGraph();
        drawCustomWeightList();

        portfolioCalc.getTotalAnnualizedStandardDeviationWeights().then(function (weights) {
            setWeights(weights);
        });

        calculateAllStats();

        
    }

    function generateFrontierGraph() {
        var frontier, assetInfo = {};

        portfolioCalc.getEfficientFrontier(0, 0.0001).then(
            function (f) {
                frontier = f;
                return portfolioCalc.getAssetCalculators();
            }
        ).then(
            function (calcs) {
                var assetRequests = [];
                calcs.forEach(function (calc) {
                    assetInfo[calc.asset.symbol] = {};

                    var request = calc.getAnnualizedMeanRate().then(
                        function (rate) {
                            assetInfo[calc.asset.symbol].MeanRate = rate;
                            return calc.getAnnualizedStandardDeviation();
                        }
                    ).then(
                        function (stdDev) {
                            assetInfo[calc.asset.symbol].StandardDeviation = stdDev;
                        }
                    );
                });
                    
                return WinJS.Promise.thenEach(assetRequests);
            }
        ).done(function() {
            drawEfficientChart(frontier, assetInfo);
        });
                
        
    }

    function drawEfficientChart(frontier, assetValues) {

        var data = [], mySeries = [], assetSeries = {};

        if (frontier.length > 0) {
            var alloc = frontier[frontier.length - 1].Allocations;

            var sortedSymbols = [];
            for (var symbol in alloc) {
                if (symbol != "id") {
                    sortedSymbols.push(symbol);
                }
            }

            sortedSymbols = _.sortBy(sortedSymbols, function (sym) {
                return alloc[sym];
            }).reverse();

            for (var sym in sortedSymbols) {
                assetSeries[sortedSymbols[sym]] = [];
            }
        }

        for (var index in frontier) {
            var opt = frontier[index];
            data.push({ x: opt.StandardDeviation, y: opt.ExpectedReturn, opt: opt });

            for (var asset in assetSeries) {
                var assetData = { x: opt.StandardDeviation, y: opt.Allocations[asset] * opt.ExpectedReturn };
                assetSeries[asset].push(assetData);
            }
        }

        for (var asset in assetSeries) {
            mySeries.push(
                {
                    name: "Weighted " + asset,
                    type: "area",
                    marker: {
                        radius: 4
                    },
                    lineWidth: 4,
                    data: assetSeries[asset]
                }
            );
        }

        mySeries.push({
            name: "Efficient Frontier",
            type: "line",
            marker: {
                radius: 8
            },
            color: "#0094ff",
            lineWidth: 6,
            data: data,
            point: {
                events: {
                    click: function (guy) {
                        var optimization = this.config.opt;
                        setWeights(optimization.Allocations);
                        $("#minrateText").val(optimization.ExpectedReturn);
                        calculateOptimal();
                    }
                }
            }
        });

        var Chart = new Highcharts.Chart({
            chart: {
                renderTo: "frontierChart",
                backgroundColor: null 
            },

            title: {
                text: null
            },

            xAxis: {
                    type: 'float',
                    title: {
                        text: 'Annualized Standard Deviation',
                        style: {
                            color: '#FFFFFF',
                            'font-weight': 400,
                            'font-family': "Segoe UI Light",
                            'font-size': "18px"
                        }
                    }
            },

            yAxis: {
                    type: 'float',
                labels: {
                    formatter: function () {
                        return Math.round(this.value * 10000) / 100 + "%";
                    }
                },
                    title: {
                        text: 'Annualized Mean Return Rate',
                        style: {
                            color: '#FFFFFF',
                            'font-weight': 400,
                            'font-family': "Segoe UI Light",
                            'font-size': "18px"
                        }
                    }
            },

            legend: {
                style: {
                    'font-family': "Segoe UI Light"
                }
            },

            tooltip: {
                style: {
                    'font-family': "Segoe UI Light"
                },
                formatter: function () {
                    var tooltip = '<b>' + this.series.name + '</b><br/><b>Mean Rate:</b> ' + (Math.round(this.y * 10000) / 100) + '%<br/><b>Standard Deviation:</b> ' + (Math.round(this.x * 10000) / 100) + '%';
                    if (this.series.name == "Efficient Frontier") {
                        tooltip += "<br /><b>Allocations:</b>";
                        var optimization = this.point.config.opt;
                        for (var symbol in optimization.Allocations) {
                            if (symbol != "id")
                                tooltip += "<br /><b>" + symbol + ":</b>" + (Math.round(optimization.Allocations[symbol] * 10000) / 100) + "%";
                        }
                    }
                    return tooltip;
                }
            },

            series: mySeries
        });
    }

    function setDate(date) {
        var pickerDate = new Date();
        if (!date) {
            date = currentState.portfolio.date;
        }
        pickerDate.setFullYear(date.year, date.month, date.date);
        startdatepicker.winControl.current = pickerDate;
    }

    function drawCorrelationTable() {
        var table = document.getElementById("correlationTable");

        while (table.hasChildNodes()) {
            table.removeChild(table.firstChild);
        }

        portfolioCalc.getCorrelationMatrix().done(function (data) {
            var header = table.createTHead();
            var row = header.insertRow(0);
            var emptyCell = row.insertCell(0);
            var i = 1;
            for (var key in data) {
                var cell = row.insertCell(i++);
                cell.className = "labelCell";
                cell.innerHTML = "<strong>" + key + "</strong>";
            }
            i = 1;
            for (var key in data) {
                var row = table.insertRow(i++);
                var labelCell = row.insertCell(0);
                labelCell.className = "labelCell";
                labelCell.innerHTML = key;
                var j = 1;
                for (var prop in data[key]) {
                    var cell = row.insertCell(j++);
                    cell.className = "dataCell";
                    cell.innerHTML = Math.round(data[key][prop] * Math.pow(10, 2)) / Math.pow(10, 2);
                }
            }
        });
    }

    function setWeights(weights) {
        for (var asset in weights) {
            if (asset != "id") {
                var id = asset + "Weight";
                var weight = weights[asset];

                document.getElementById(id).setAttribute("value", weight);
            }
        }
    }

    function drawCustomWeightList() {
        var weightDiv = document.getElementById("assetWeights");

        while (weightDiv.hasChildNodes()) {
            weightDiv.removeChild(weightDiv.firstChild);
        }

        var header = document.createElement("h2");
        var hr = document.createElement("hr");
        header.innerText = "Asset Custom Weights";

        weightDiv.appendChild(header);
        weightDiv.appendChild(hr);

        var table = document.createElement("table");

        var i = 0;
        for (var asset in portfolioCalc.portfolio.assets) {
            var row = table.insertRow(i++);
            var labelCell = row.insertCell(0);
            var weightCell = row.insertCell(1);
            var assetName = portfolioCalc.portfolio.assets[asset];
            var newlabel = document.createElement("label");
            var newtext = document.createElement("input");

            var id = assetName + "Weight";
            newtext.setAttribute("id", id);
            newtext.setAttribute("type", "text");
            newlabel.setAttribute("for", id);
            newlabel.innerText = assetName + ": ";

            labelCell.appendChild(newlabel);
            weightCell.appendChild(newtext);
        }

        weightDiv.appendChild(table);
        
    }

    function setupWeights() {
        weights = {};

        for (var asset in portfolioCalc.portfolio.assets) {
            var assetName = portfolioCalc.portfolio.assets[asset];
            var text = $("#" + assetName + "Weight").val();
            weights[assetName] = Number(text);
        }

        var allZeros = true;
        for (var key in weights) {
            if (weights[key] != 0) {
                allZeros = false;
                break;
            }
        }

        var hasNaN = false;
        for (var key in weights) {
            if (isNaN(weights[key])) {
                hasNaN = true;
                break;
            }
        }

        return !(allZeros || hasNaN);
    }

    function calculateHistorical() {
        if (!setupWeights()) {
            portRate.innerText = "---";
            portStdev.innerText = "---";
            portCorrelation.innerText = "---";
            portBeta.innerText = "---";
            portSharpe.innerText = "---";
        }
        else {
            portfolioCalc.getAnnualizedMeanRate(weights).then(
                function (value) {
                    portRate.innerText = Math.round((value * 100) * Math.pow(10, 2)) / Math.pow(10, 2) + "%";
                    return portfolioCalc.getAnnualizedStandardDeviation(weights);
                }
            ).then(
                function (value) {
                    portStdev.innerText = Math.round((value * 100) * Math.pow(10, 2)) / Math.pow(10, 2) + "%";
                    return portfolioCalc.getMarketCorrelation(weights);
                }
            ).then(
                function (value) {
                    portCorrelation.innerText = Math.round(value * Math.pow(10, 2)) / Math.pow(10, 2);
                    return portfolioCalc.getBeta(weights);
                }
            ).then(
                function (value) {
                    portBeta.innerText = Math.round(value * Math.pow(10, 2)) / Math.pow(10, 2);
                    return portfolioCalc.getSharpe(weights);
                }
            ).done(
                function (value) {
                    portSharpe.innerText = Math.round(value * Math.pow(10, 2)) / Math.pow(10, 2);
                }
            );
        }
    }

    function calculateCAPM() {
        var marketRate;
        var riskFreeRate;
        var marketstr = $("#marketText").val();
        var excessstr = $("#excessText").val();

        if (marketstr != undefined && marketstr != "") {
            marketRate = Number(marketstr);
        }
        if (excessstr != undefined && excessstr != "") {
            riskFreeRate = Number(excessstr);
        }

        if ((marketRate == undefined || isNaN(marketRate))) {
            expRate.innerText = "---";
            expBeta.innerText = "---";
            expCorr.innerText = "---";
            expStdev.innerText = "---";
            expSharpe.innerText = "---";
        }
        else {
            var newweights;
            if ((riskFreeRate == undefined || isNaN(riskFreeRate))) {
                riskFreeRate = 0;
            }
            var mrp = marketRate - riskFreeRate;
            portfolioCalc.optimizeCAPMAllocations(0, riskFreeRate, mrp).then(
                function (result) {
                    newweights = result.Allocations;
                    return portfolioCalc.getCAPMAnnualizedMeanRate(newweights, riskFreeRate, mrp);
                }
            ).then(
                function (value) {
                    expRate.innerText = Math.round((value * 100) * Math.pow(10, 2)) / Math.pow(10, 2) + "%";
                    return portfolioCalc.getAnnualizedStandardDeviation(newweights);
                }
            ).then(
                function (value) {
                    expStdev.innerText = Math.round((value * 100) * Math.pow(10, 2)) / Math.pow(10, 2) + "%";
                    return portfolioCalc.getMarketCorrelation(newweights);
                }
            ).then(
                function (value) {
                    expCorr.innerText = Math.round(value * Math.pow(10, 2)) / Math.pow(10, 2);
                    return portfolioCalc.getBeta(newweights);
                }
            ).then(
                function (value) {
                    expBeta.innerText = Math.round(value * Math.pow(10, 2)) / Math.pow(10, 2);
                    return portfolioCalc.getCAPMSharpe(newweights, riskFreeRate, mrp);
                }
            ).done(
                function (value) {
                    expSharpe.innerText = Math.round(value * Math.pow(10, 2)) / Math.pow(10, 2);
                }
            );
        }
    }

    function calculateOptimal() {
        var minimumRate;
        var minratestr = $("#minrateText").val();

        if (minratestr != undefined && minratestr != "") {
            minimumRate = Number($("#minrateText").val());
        }
        
        if (minimumRate != undefined && !isNaN(minimumRate)) {
            var newweights;
            portfolioCalc.optimizeAllocations(minimumRate).then(
                function (result) {
                    newweights = result.Allocations;
                    return portfolioCalc.getAnnualizedMeanRate(newweights);
                }
            ).then(
                function (value) {
                    optRate.innerText = Math.round((value * 100) * Math.pow(10, 2)) / Math.pow(10, 2) + "%";
                    return portfolioCalc.getAnnualizedStandardDeviation(newweights);
                }
            ).then(
                function (value) {
                    optStdev.innerText = Math.round((value * 100) * Math.pow(10, 2)) / Math.pow(10, 2) + "%";
                    return portfolioCalc.getMarketCorrelation(newweights);
                }
            ).then(
                function (value) {
                    optCorr.innerText = Math.round(value * Math.pow(10, 2)) / Math.pow(10, 2);
                    return portfolioCalc.getBeta(newweights);
                }
            ).then(
                function (value) {
                    optBeta.innerText = Math.round(value * Math.pow(10, 2)) / Math.pow(10, 2);
                    return portfolioCalc.getSharpe(newweights);
                }
            ).done(
                function (value) {
                    optSharpe.innerText = Math.round(value * Math.pow(10, 2)) / Math.pow(10, 2);
                }
            );
        }
        else {
            optRate.innerText = "---";
            optStdev.innerText = "---";
            optCorr.innerText = "---";
            optBeta.innerText = "---";
            optSharpe.innerText = "---";
        }
    }

    function calculateAllStats() {
        calculateHistorical();
        calculateCAPM();
        calculateOptimal();
    }

    function navFromList(eventInfo) {
        eventInfo.detail.itemPromise.done(function (item) {
            var datepicker = document.getElementById("startdatepicker");
            portfolioCalc.getAssetCalculator(item.data.symbol).then(function (calc) { WinJS.Navigation.navigate("/pages/assetspecs/assetspecs.html", { symbol: item.data.symbol, startDate: datepicker.winControl._currentDate, calculator: calc }); });
        }); 
    }

    // Command and Flyout functions.
    function showAddSymbolFlyout() {
        showFlyout(addSymbolFlyout, addButton, "top");
    }
    function showFlyout(flyout, anchor, placement) {
        flyout.winControl.show(anchor, placement);
    }

    function hideAddSymbolFlyout() {
        hideFlyout(addSymbolFlyout);
    }
    function hideFlyout(flyout) {
        flyout.winControl.hide();
    }


    function doClickAdd() {
        showAddSymbolFlyout();
    }

    function addSymbol(event) {
        var text = symbolInput.value;

        publicMembers.DataSource.add(text);

        hideAddSymbolFlyout();

        portfolioCalc.getMinDate().done(
            function (date) {
                setDate(date);
            }
        );

        
        refresh();
    }
})();
